Hi,欢迎来到 卡瓦邦噶!我是 laixintao,现在生活在新加坡。我的工作是 SRE,喜欢在终端完成大部分工作,对各种技术都感兴趣。我从 2013 年开始写这个博客,写的内容很广泛,运维的方法论,编程的思考,工作的感悟,除了技术内容之外,还会分享一些读书感想,旅行游记,电影和音乐等。欢迎留下你的评论。

声明:本博客内容仅代表本人观点,和我的雇主无关。本博客承诺不含有 AI 生成的内容,所有内容未加说明均为博主原创,一经发布自动进入公有领域,本人放弃所有权利。转载无需本人同意。但是依然建议在转载的时候留下本博客的链接,因为这里的很多内容在发布之后会还会不断地继续更新和追加内容。 Not By AI

Piccolo P2P 镜像分发

我们遇到的 Harbor 的另外一个问题是 image 的下载瓶颈。在容灾的时候,我们需要在短时间内启动几万个容器。Harbor 这里就成了瓶颈,抛开所有的数据库和文件系统的瓶颈不说,网络这里就需要 Tib 级别的带宽。这是不现实的。

用户构建的 image 质量参差不齐,大于 10GiB 的 image 比比皆是,尽管这些 image 都有很大的优化空间,但是期望所有的用户都按照构建 image 的最佳实践1进行优化,也是不现实的。

于是问题就成了:如何才能够大量的 worker 节点迅速扩容上万的容器?

此外,在平时,遇到工作日多个系统发布的时候,或者整点的时候运行定时任务(定时任务每次都会下载 image),harbor 的压力也非常大,经常满载运行。如果能解决这个问题,平时的负载问题也可以缓解。

之前介绍过 Spegel 的下载方案2,Spegel 的想法很好,基本思想是,先去其他已经存在这个 image 的机器上去下载,如果找不到,再 fallback 到 Harbor 下载。可惜的是,Spegel 把 P2P 下载和 P2P 服务发现混为一谈了,导致服务发现的性能极低。

什么意思呢?去其他的机器上下载 image,需要的一个信息是:哪一个机器有这个 image?这就是下载源的服务发现。这个服务发现和 P2P 本质上没有什么关系,用什么都可以。P2P 技术解决的是下载上的瓶颈,只有能有一个方法记录每一个机器上现在都有什么 image 就可以,用 Etcd 也可以,不一定也得用 P2P 形式的。

但是 Spegel 这里是用 P2P (libp2p)做的服务发现。在我看来这是完全没有必要的。我们在实际的部署中遇到的问题有:

  • 服务发现的 latency 高,现在的配置是 30s,还是会有超时,P2P 本质上是在一个不稳定的分布式网络中寻找一个资源,效率不高;
  • 成功率低,在 P2P 网络里面,能否发现一个 key 是概率性问题,不是确定的3
  • 删除 image 不会分发到 p2p 网络中,必须有访问事件得到 404 然后触发清除,这是 libp2p 本身的设计决定的,这又会导致服务发现的错误率高;

在实际的部署中,Spegel 的缓存命中率在 25% 左右。

当然,使用 P2P 做服务发现也是有好处的,好处就是部署简单,不需要额外的存储依赖。只不过这个好处和它带来的问题相比就微不足道了。

我觉得如果解决服务发现的问题,使用 P2P 下载的方法,是可以解决资源的瓶颈问题的。

所以设计的方案是:使用中心化的,高性能的服务发现,去中心化的 P2P image 下载。

原本 Spegel 的代码,服务发现层是独立的模块,所以我从 fork 它的代码来添加一个新的服务发现方式开始。但是随着修改,发现问题越来越多,比如 subscribe containerd events 的时候没有正确 defer 关闭,没处理好 containerd 重启的情况,等等,最后开始了一个独立的仓库,叫 Piccolo。代码4依然是开源的,但是还没时间写文档和注释,这篇博客先写一下原理。

项目主要分两部分:

Pi – 安装在每台机器上的 daemonset,负责:

  • 作为 containerd 的本地 mirror,当 containerd 需要下载 image 的时候,会先尝试本地的 Pi 端口,如果得到 404 (或者 5xx),再尝试下一个下载 mirror,一般是 dragonfly,最后是 harbor source;
  • 连接本地的 containerd,跟踪本地的 image 状态,本地的 image 增加或者减少,报告给 Piccolo server;
  • 连接本地的 containerd,其他的 Pi 发送来下载 image 请求的时候,上传本地有的 image。

Piccolo Server 是服务发现的源,全局只有一组(几个实例就够用了),提供 3 个接口:

  • Advertise:其他 Pi 上报的 image 列表,存储到 MySQL;
  • Findkey:Pi 来询问一个 image(实际上是 image 的 manifest 和 blob)在哪里有的时候,回复地址列表;
  • Sync:其他 Pi 可以用这个接口做全量同步(定时,以保证 image 总是最终一致的);
Piccolo 的架构图

部署之后,97% 的下载请求可以在 Pi 完成而不必请求 Harbor。

Harbor 的流量对比,绿色的线是逐步发布 Piccolo 的流量,黄色线是之前的日常流量。

从这个监控可以看出,随着 Piccolo 的发布,Harbor 的流量骤降。而且每个小时的峰值流量也几乎没有了。

Pi 部署在每一个机器上,使用的资源也非常少,平均 CPU 用了一个 core 的不到 1%,可以忽略不计。平均内存用了 22M 左右,也可以忽略不计。

Piccolo 为集群提供了大约 8Tib 的下载带宽,没有消耗额外的资源,几乎是免费的 8Tib 带宽。

Piccolo server 方面,性能也很高,一台 8C8G 的 instance 足以支撑 5 万个 Pi (实际的物理机 worker 节点)。秘诀就是注重细节的性能优化。

比如全量同步的资源消耗较大,一起部署的机器会定时发送 keeplive,通过对这些定时执行的 API 加随机偏移,可以保持这些 API 的频率几乎是均匀的,解决了资源的峰值问题。

服务发现的核心,是用一张 MySQL 表,存储了 blob 和 IP 的对应关系。服务发现请求主要是通过这张表的查询完成的。Piccolo 支持把不同的 group(同一个 group 的 Pi 可以互相发现,不同的 group 的 Pi 不可以互相发现。其实这个功能也可以通过部署多个 Piccolo Server 来实现)放到不同的数据库中,加上索引优化(极致的索引优化,所有的查询都是 index-only 的),每一个库 2千万的数据,请求在 200 QPS,耗时在 10ms 以内,已经足够使用了。

Piccolo server 在服务发现接口返回的时候,会根据请求者的 IP 地址,把所有的资源拥有者的 IP,根据和请求者的 IP 相似度(距离)排序,返回。这样 Pi 在下载的时候,会从距离和它最近的邻居开始尝试,这样可以最大程度减少跨网络设备的带宽流量。

所有的 API 接口都有重试和指数时间退让,这样在大规模部署的时候,可以分散一些请求,不至于大家一起失败。

在高可用方面,由于 Piccolo server 是无状态的,所以部署多个实例即可。在预防未知的 bug 上,系统的每一个阶段都是可以降级的:

  • 如果 containerd 从 Pi 下载失败,会 fallback 到下一个下载源;
  • 如果 Pi 从一个 Pi 下载失败,会继续尝试下一个 Pi,直到超时;
  • 如果 Pi 访问 Piccolo 失败,会等待并重试,直到超时;

  1. 这里是之前 blog 过的一些技巧 Docker 镜像构建的一些技巧 ↩︎
  2. Spegel 镜像分发介绍,也讨论了一些其他的可能方案,比如 dragonfly,或者 lazy loading ↩︎
  3. https://en.wikipedia.org/wiki/Kademlia ↩︎
  4. Github 地址:https://github.com/laixintao/piccolo ↩︎
 

一起看电影

我和欣都很喜欢看电影,我们一起看了很多电影。我最喜欢的导演是韦斯·安得森,现在最喜欢的电影是《布达佩斯大饭店》,看了很多遍。欣最喜欢的电影是《沙丘》,也看了很多遍。科幻电影是我们共同喜欢的类型。

《布达佩斯大饭店》每一帧都很美,故事性也很强,台词简洁但是话又很多。和其他的电影很不一样,后来我才知道,这属于「艺术电影」的范畴。导演的个人特色太鲜明,有一次我在看《腓尼基计划》,欣看了一眼,就问,「这又是那个怪导演拍的?」

《沙丘》作为老牌科幻,背景世界构建的宏大而又符合逻辑,导演 Denis Villeneuve 实力很强,把这么难拍的电影也拍的像艺术一样。每一帧也都很美。

我们在一起看了很多电影。用过各种各样的设备和方法一起看。

高铁从上海到山东需要 5 个小时,看电影是最好的消磨时间的方法了。问题是,一直没有找到比较好的可以两个人一起看电影的方案。即两个人一起看,但是各自用自己的降噪耳机。

在网上找到了一个 Mac 可以使用的方法1,原理是用 Audio MIDI 创建一个新的输出设备,实际输出的物理设备是 2 个耳机,步骤如下:

  1. First, on your Mac, go to System Preferences > Bluetooth. Turn it on and pair the two pair of AirPods with your Mac. Now only one pair of AirPods can be connected.
    Now open Finder and click on Applications > Utilities > Audio MIDI Setup.
  2. Look for the plus sign and click on it to create multi-output device.
  3. Check the box next to the two pairs of AirPods and check the Drift Correction box next to the second pair of AirPods.
  4. Click on System Preference > Sound, followed by Multi-Output Device.
  5. Once done, the audio of your Mac will be sent to both pairs of AirPods. So this how to connect multiple AirPods to Mac.

有的时候不想拿出来 Mac,或者有时候没有带,就用下面这个万能的办法,不过仅适用于提前下载好的电影:

  1. 两个人的各自用个自己的设备(2个 ipad),以及各自的耳机观看;
  2. 调整进度条到同步的位置。

这样也算一起看了。

近几年大部分时间都是在家里一起看,买过 HBO,Amazon Prime,Disney+,Netflix 等等,在电视上看了不少电视和剧,简单也方便,体验很不错。《克拉克森的农场》,《Severance》,《浴血黑帮》,《大西洋帝国》,《For All Mankind》,等等。

去年买了一个新的电视,型号是三星的 S95F,可能是最近几年买过的最满意的电子产品了,OLED 屏幕非常惊艳,这一款还有一个不反光的特性,白天看也不用拉窗帘。最近又发现一个惊艳的功能。

有天家里有人睡觉了,我戴着耳机在客厅里看电视,欣过来问我,你看电视怎么没有声音?我说用的耳机。欣惊讶到,这电视居然能连接蓝牙耳机。我觉得这是很正常的功能。

于是她拿出来自己的耳机要和我一起看,我说不能连接两个耳机。她说可以,这是正常的功能。

我觉得肯定不行,因为之前用的所有的设备在不 hack 的情况下都不支持同时输出到 2 个蓝牙设备(实际上,iOS 在 2019 年好像就支持 audio share 了)。但是决定还是用事实证明。于是我打开蓝牙,连接上她的耳机。预期是之前连接的我的耳机会断开。结果两个耳机都连接成功。

然后打开声音输出,预期是只能选择一个耳机。结果电视自动弹出来对话框,问是否要切换到 multiconnect 功能?选择「是」之后,两个耳机都完美地有了声音。

三星的 dual audio 功能,不光可以输出声音到两个设备,屏幕居然也可以分割成两部分,同时看2个甚至多个节目。电视也支持虚拟化了。

后来一想,这个功能确实挺符合直觉并且实用的。现在的问题是,为什么之前那么多设备反而不支持?

  1. 详细教程:https://www.tenorshare.com/iphone-tips/how-to-connect-two-airpods-to-one-phone.html ↩︎
 

《征服C指针》

最近读了《征服C指针》,是一本好书。日本人写的计算机技术书籍像娓娓道来的技术博客那样,假设读者没有(或者有很少)相关的背景知识,围绕一个主题,除了介绍干巴巴的知识,还会谈自己的经验和理解,对其他的书和论点做点评,指出一些常见的谬误,这样读者可以更容易理解书里讲的东西。如果只有干巴巴的知识,那么去读维基百科就好了。但是维基百科(无论中文还是英文)的说明一般晦涩难懂,因为百科的首要目标是准确,消除歧义,而不是易于理解。而好的技术书里面会旁征博引,运用举例和比喻,用多的篇幅详细说明难懂的部分,加上作者的经验让读者知道哪些内容是经常使用的,哪些是晦涩又不常用的,哪些是可以辅以技巧理解的。这是我理解的优秀的技术书籍。比如,《流畅的 Python》就是这样一本好书,这本书不光介绍 Python 的编程知识,还有对 Python 语言设计的点评,和其他编程语言的对比,作者自己的理解和评价,读完之后对于整个编程概念的理解都会有提升。

这本书其实就不只是 C 语言的指针,还设计内存分配,C 的语法和编译器,CPU 等知识。从初学者到老手都可以从中有所收获。

另外书中对于有些概念可以给出确定的定义,而不是给出模棱两可的答案。如果永远模棱两可,那么永远都不会错,但是这种内容读起来也是浪费时间。一位得高望重的星际2游戏解说曾经说过:「专业解说要敢于下判断。」

以下是一些笔记。每一段引用都是书中的原文,引用如果不是连续的,就来自于不同的段落。非引用的格式是我的评论。

在C语言中,记录指针指向何种类型是只到编译器为止的,到了运行的时候就已经没有相关信息了。在运行时,指针的值就只是单纯的地址而已。​“要从这个地址里取出哪种类型的值”这一信息只残留在编译器生成的机器码中。无论是在指针的值中,还是在指针指向的变量的内存空间中,都没有类型的信息。因此,如果把指向int的指针转换成了void*,就不可能再知道它原来是指向 int 的了。

指针的类型,主要是为了告诉编译器信息。

会报错如下:

只告知了内存上的地址,却没有告知那里保存的是什么类型的数据,当然无法读取。

改成下面这样可以运行:

这正是指针运算的特征。在 C 语言中,对指针加1 后,其地址就增加该指针所指向的类型的长度。示例程序中的 hoge_p 是指向 int 的指针,而在我的环境中 int 的长度是 4,所以对地址来说,加 1 就是前进 4 字节,加 3 就是前进 12 字节。

对指针+1,地址移动的单位是「指针所指向的类型的长度」,这个也是编译器计算的,这是指针需要类型的主要原因。

由于空指针可以确保与任何非空指针进行比较都不相等,所以经常作为返回指针的函数发生异常时的返回值使用。

例如我工作的地方位于日本名古屋市某栋大楼的 5 楼,某人爬一层楼需要 10秒,那么从地面上到 5 楼需要花费多少秒?50 秒?很遗憾,正确答案是 40秒。想必大家在中学都学过等差数列,等差数列的第 n 项等于“首项 + 公差× (n –1)”​。每个都要减 1,真麻烦……此外,​“1900 年代”并不是 19 世纪,它的一大半属于 20 世纪。更加复杂的情况是,2000 年不属于 21 世纪,而属于 20 世纪。这些问题分别可通过以下方式回避。把大楼里与地面等高的那层计作第 0 层把数列的首项计作第 0 项把最初的世纪计作 0 世纪,把公历最初的年份计作 0 年这种“差 1 错误”的问题在编程中经常发生。因此,普遍认为在一般情况下如果以 0 为基准编号,那么通常(并不是所有)能回避这类问题。

延伸阅读:为什么要“包含头不包含尾”?

要点

p[i] 是 *(p + i) 的简便写法。

下标运算符 [​] 只有它原本的意义,与数组毫无关系。

要点

【比上面的要点更重要的要点】

但是,千万别写成那样。

在 get_word() 中使用下标运算符访问 buf 的内容,会让人觉得从 main() 传递过来的就是buf 数组。然而,这是个错觉,从 main() 传递过来的说到底只是指向 buf 的初始元素的指针。

函数传递只能传递指针而不能传递数组。即使数组和指针不同,但是在函数传递的时候,数组也会转换为指针,指向数组开头的元素。

在 C 语言中,参数全部都是通过值传递的。

即便是像全局变量那样在函数外部定义的变量,一旦加上 static,其作用域就只限定在当前源文件内。指定为 static 的变量(函数)对于其他源文件是不可见的(函数也是一样的)​。

另外,对于函数(非 static 限定)和全局变量,只要名称相同,即便位于不同的源文件中,也会被当作相同的对象处理。

因此,根据操作系统及CPU的不同,需要规定不同的调用方法,这就叫作调用约定(calling convention)​。本书中说明的调用方法是在x86系列处理器中被称为cdecl的调用约定。该方法中所有的参数都通过压栈的方式进行传递。

malloc() 会遍历链表,搜寻空块,若该块大小足够,就将其分割出来,做成使用中的块,并向应用程序返回紧邻管理区域的下一个地址。free() 会改写管理区域的标志,将该块置为空块,如果上下有空块,就顺便将它们合并成一个块。这是为了防止块碎片化。 这种操作称为 coalescing。当没有足够大的空块满足 malloc() 的要求时,就向操作系统请求(在 UNIX 中需要通过 brk()系统调用)扩充内存空间。

补充 

Valgrind正如前面多次提到的那样,与动态内存分配相关的Bug往往出现在距离它被发现的位置很远的地方,因此调试非常困难。在Linux上可以使用Valgrind工具追踪这类Bug。Valgrind工具用于检测对malloc()分配的内存空间越界读写、忘记free()(内存泄漏)或者对同一块内存空间多次free()这类问题。

如果运气好,标准库 glibc 也可以为我们检测出这个问题。

调用 malloc() 之后必定写上相应的free() 是一种谨慎的编程风格。程序员就应该小心翼翼地将 malloc()和 free() 对应起来。“因为调用了 exit(),所以就没必要free() 了”的想法是不负责任的偷工减料行为,是不良的编程风格。不管怎么说,程序员也是人,人就是这么一种在可能犯错的地方必定会犯错的生物。可是,​“必须 free() 派”却偏要大肆宣扬无论如何都要“谨慎地”编码,这种论调其实是于事无补的。

我认为,​“谨慎地”编码并没有什么了不起的,那些能够尽可能地回避“麻烦事”的人才是优秀的程序员。在我心中,理想的程序员是下面这样的:在能够安全地偷懒的地方尽可能地偷懒,并且尽可能地依靠工具而不是肉眼来进行检查,但在无论如何都需要人工处理麻烦的事情时,会在心中坚定地起誓“总有一天要将它自动化”​。

这是我最喜欢的一段话。Python 的初学者写的代码,会在所有的函数入口都写上 try-catch,称之为防御性编程。我觉得这么做的人肯定会有这样的疑惑:「怎么这么麻烦?」对于这个疑惑,有两类人,一类是认为「这么做一定有道理,作为程序员我们要不辞劳苦地做好工作。」另一类人认为「一定有更方便的方式」。

正如图 2-17 所示,填充有时会被放到结构体的末尾。因为在创建结构体数组时,填充是必要的。在将 sizeof 运算符应用到这样的结构体上时,返回的是包含末尾填充部分大小的长度。将结果和元素个数相乘,就可以获取数组整体的长度。

——有关结构体的对齐

小端与大端到底哪一种更好呢?这个话题经常引起人们的争论,此处就不再深入讨论了。它们各有各的优点。人类在用纸和笔做加法时也会从低位开始相加,所以对 CPU 来说,或许采用小端的方式更轻松一些,而在人类看来,大端的方式或许更容易理解。

Little Endian vs Big Endian

C 语言的声明要用英语阅读

我们可以遵循以下步骤解释 C 语言声明。

先看标识符(变量名或函数名)​。

从贴近标识符的地方开始,按照如下优先级解释派生类型(指针、数组、函数)​:

①用于整合声明的括号;

②表示数组的 [​]、表示函数的 ();

③表示指针的 *。

完成对派生类型的解释之后,通过 of、to 或returning 连接句子。添加类型修饰符(位于左侧,比如 int、double)​。如果不擅长英语,可以用中文解释。

能正确地阅读 C 指针的声明,函数参数中有关指针的声明,已经 sizeof 中的声明,是读此书最大的收获了。

像这样可以确定长度的类型,在标准中被称为对象类型(object type)​。然而,函数类型不是对象类型。C 语言中不存在函数类型的变量,因而我们无法(也没必要)确定其长度。我们说过,数组类型是由若干个派生源类型排列而成的类型。因此,数组类型的总长度为:

派生源类型的长度×数组的元素个数

但是,由于函数类型的长度无法确定,所以也就无法从函数类型派生出数组类型。也就是说,无法创造出“函数的数组”这种类型。但是,可以生成“指向函数的指针”这一类型。只是指向函数类型的指针是不能进行指针运算的,因为我们无法确定指针指向的类型的长度。

当表达式代表的是某处的存储空间时,该表达式就称为左值。与此相对,当表达式仅代表值时,该表达式称为右值。表达式中有时存在左值,有时不存在左值。例如,变量名是左值,而 5 这样的常量、1 +hoge 这样使用运算符的表达式就不是左值。

当作为 sizeof 运算符的操作数时

在以“sizeof 表达式”的形式使用 sizeof 运算符时,由于这里的操作数是表达式,所以即使是对数组使用 sizeof,数组也会被解读为指针,从而只能获取指针的长度——或许有人是这样认为的,但其实在数组作为 sizeof 运算符的操作数的情况下,将数组解读为指针这一规则是无效的,在这种情况下返回的是数组整体的长度。

总之,关于指向函数的指针的 C 语言的语法是比较混乱的。造成这种混乱的罪魁祸首就是“函数在表达式中会被解读为指向函数的指针”这一意图不明(难不成是为了与数组保持一致?​)的规则。

在表达式中,数组会被解读为指向该数组初始元素的指针,因此代码可以写成下面这样。

但是,反过来写成下面这样就不行。array = p;

数组和指针截然不同。

“不要误会我对 goto 语句持有任何教条主义的执念。我只是担忧,很多人把这件事给神化了,甚至认为仅凭某个编程技巧或某个简单的编程原则,就能解决编程语言的概念问题!”

 

2025

在新加坡的第五年整。

今年和欣好像一直在找房子。之前住在湖畔的一个 HDB,房东决定要卖房子,所以我们不续租了,从湖畔的 HDB 搬到了碧山的 Condo。住了不到一年,年底又搬了一次家。第一次住完全没有家具的房子,又花了时间去买沙发,床,桌子,组装家具,搬家,年底要处理的事情太多太多。

买了一台大电视,三星的 S95F,被惊艳到了,OLED 屏幕,还有不反光的特性,效果非常好。于是年底的假期大部分时间都和欣窝在沙发上看电视,买了 HBO,把过去看过的很多电影又看了一遍。把《权力的游戏》也从头开始看了一遍。

今年玩的游戏不多,为了准备玩《GTA 6》买了一台电脑放在客厅,但是这游戏居然又跳票了。缝合怪《潜水员戴夫》拿到了完美通关,《荒野大镖客2》玩到了 50% 左右,然后和欣一起玩《双人成行》,还没有通关。

又买了一台电子书设备和微信读书会员,看了莫言的《檀香刑》,《酒国》以及一些杂文集,一些余华和刘震云的小说,以及其他一些专业书。

学会了一个新的技能:双拼。不过感觉刚刚够得到之前全拼的速度,再用一段时间速度应该更快。

旅行。年前我和欣带爸妈来新加坡和普吉岛旅行。普吉岛去过 3 次了,但是一次博客都没有写过,拖着拖着就有一些原因不想写了。9 月份去上海「旅行」了一趟。在上海生活了这么多年,其实并没有作为游客去过一些地方,这次回去,体验了脱口秀(笑得肚子疼),逛了南京路(和当初上学的时候很不一样了,傣妹居然还活着,其他的店大部分都换了),看了一个剧。年底准备了关西的旅行,但是因为其他事情取消了。

工作方面

今年工作上的难度越来越高。我比较擅长用技术来解决问题,但是今年公司的管理风格向重视流程转移,试图通过流程上的规范来提高整体的可用性,导致审批越来越多,流程越来越复杂,一个简单的 API 调用需要花费之前数倍的成本来实现。

另一方面,在金融方面的发展带来了更多的合规要求,从而带来了巨量的运维工作。可高层在「降本增效」方面的努力没有停止的迹象,仅仅靠运维工作是没有「绩效」的,如果要拿到比较好的绩效以及晋升,就需要拿出时间去做亮点项目。这就造成了另一个矛盾点,也造成了另一部分人的离职。身边的同事越来越少。

在《凤凰项目》一书中,安全部门的主管发现项目不需要安全团队就可以通过一项审计,意识到自己的工作可有可无,在酒吧喝得酩酊大醉。他问运维部门的主管:「我们就真的对你们一点帮助也没有吗?」运维主管尽管很想安慰他,但是又不想说谎,只好说:「对不起,一点也没有。」

每当安全团队要我们把一个毫无敏感信息的 API 用最高的安全等级来要求的时候,我就会想起来这一段。

尽管如此,今年还是做了不少值得骄傲的事情:

  • 接手 Harbor 之后完成了 GC 的自动化,解决了数年之久的一个痛点1
  • 实现了镜像的 P2P 下载,彻底解决了镜像下载的瓶颈问题,之后写博客介绍一下技术细节吧;
  • 新开始了一个内部的标准化项目;

在产品方面,不再负责 Service Mesh 项目了。我从加入公司就开始维护这个项目,经历了几十个版本,也经历过它造成的(我加入之后)公司最大的故障,经历了几代开发团队变迁。开发团队的同事技术方面无可挑剔,产品在近几年没有出过重大事故,也支持了业务的需求。不足在产品的易用性上,配置过于复杂,难以理解,学习成本高。整体架构需要改变,现在是 daemonset 部署模式,难以实现资源隔离和审计。

产品运维的工作中心转到了 SDN 上面,也是我感兴趣的方面。SDN 也有很多问题:易用性和控制面的可用性太差;组件太多过于复杂;需要硬件兼容软件而不是软件兼容硬件,等等。这些都是需要解决的问题。

明年的工作会尝试一下结合 eBPF 来做架构上的可观测性,通过图数据库自动整理软件的依赖。

就写这么多吧,明年还是继续在网络的领域耕耘,学习和分享更多的知识。

  1. Harbor GC 问题 ↩︎

其他的年终总结列表:

  1. 2013年
  2. 2014年
  3. 2015年
  4. 2016年
  5. 2017年
  6. 2018年
  7. 2019年
  8. 2020年
  9. 2021年
  10. 2022年
  11. 2023年
  12. 2024年
 

我的姥姥

我的姥姥今年去世了,没有痛苦。我们之前都预料到了这一天的到来,大家在五月份都陆续去看望了她,大家也都知道这是告别。

小姨十年前告诉我,姥姥诊断出了肺癌。但是年纪太大,家里商量之后,决定不治疗,让她安享晚年,想吃什么就吃什么,想做什么就做什么。姥姥晚年喜欢抽烟,吃肉。每次去看望姥姥,我都带两条烟,十斤肉,或者红包。

这几年每一次去看姥姥,她下炕的次数越来越少,头发越来越花白,和她说话的声音需要越来越大。

姥姥的去世,我没有为她感到伤心,只为我以后再也见不到她了而伤心。至少她的晚年可以抽烟吃肉,没有把时间都花在了 ICU 里面。《长命百岁》里面提到, 现代医学专注于延长人类的寿命,但是这些寿命的质量也同样重要。人固有一死,我希望自己离开世界的时候也可以这样。

十年之后,姨们过年聚在一起,终于觉得当初的诊断不对,应该不是肺癌。但是也没有必要再去纠结了,管它是什么吧。

姥姥裹小脚,有六个女儿,一个儿子,重男轻女,我上高中的时候,过年的压岁钱姥姥就只给我和哥哥了,姐姐和妹妹没有。到了晚年,姥姥和姥爷俩人依然住在那诸城县边上的一个村子里,在那里生活了近一个世纪。姥姥和姥爷的感情是很多人都羡慕的。

姥姥的一生几乎都住在那几间房子里,没有去过太远的地方,可能没有出过省,没有吃过什么山珍海味。但也许她的一生是幸福的。这又让我思考起来那个这一年我不断思考的问题:「怎么度过这一生才是值得的呢?」

一路走好,姥姥。